home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / graphics / fig2mf / graphbase.mf < prev    next >
Text File  |  1993-03-18  |  25KB  |  1,278 lines

  1. %%% Date: Mon, 12 Oct 92 22:32:32 EST
  2. %%% Message-Id: <9210121232.AA18641@ee.latrobe.edu.au>
  3. %%% To: ajs@msdrl.com
  4. %%% Subject: graphbase.mf 0.2 gt mod 1 Mon 12 Oct 1992.
  5. %%% Cc: ecsgrt@ee.latrobe.edu.au
  6. %%% Status: OR
  7. %%% 
  8. %%% 12:10 GMT Mon 12 Oct 1992 - Geoffrey Tobin.
  9. %%% 
  10. %%% Dear Anthony,
  11. %%% 
  12. %%% Latest mod of "graphbase.mf".
  13. %%% 
  14. %%% This is still a long way from what it ought to be for Fig 2.1, but
  15. %%% maybe you can use some of the new code.  Much of it is comments!
  16. %%% 
  17. %%% I've modified onedot based on Knuth's "drawdot", written a hatchpath
  18. %%% macro to complement shadepath, attempted a gray level formula to match
  19. %%% human light sensitivity, some macros to draw as well as shade or
  20. %%% hatch, and an arrowpath macro to draw an arrow for arbitrary path and
  21. %%% to draw that path.
  22. %%% 
  23. %%% THe 3-point arc code is included, with arcthreearrow to draw an arrow
  24. %%% as well.
  25. %%% 
  26. %%% Some code for drawing Interpolated Splines is included.  The macros to
  27. %%% test are ispline, isplinearrow, and isplineshade.
  28. %%% 
  29. %%% Backward arrows are on my agenda.
  30. %%% 
  31. %%% All the Best!
  32. %%% Geoffrey Tobin
  33. %%% 
  34. %%%
  35. %%%  File: graphbase.mf
  36. %%%
  37.  
  38. mode_setup;
  39. message "mfpic version 0.2 graphbase - gt mod 1 - 10:16 GMT Mon 12 Oct 1992";
  40.  
  41. % set up local environment
  42.  
  43. def mfpicenv =
  44. begingroup
  45.  
  46. % miscellaneous utilities
  47.  
  48. % gt - op_pair operates with "op"
  49. % on both parts of a pair p.
  50.  
  51. save op_pair;
  52.  
  53. vardef op_pair (text op) (expr p) =
  54.   (op (xpart p), op (ypart p))
  55. enddef;
  56.  
  57. save floorpair, ceilingpair;
  58.  
  59. def floorpair = op_pair (floor) enddef;
  60. def ceilingpair = op_pair (ceiling) enddef;
  61.  
  62. % gt - Should there be more error-checking,
  63. % eg of types, in these utility routines?
  64. % That would slow them down.
  65.  
  66. % gt - textpairs converts the text t into the
  67. % array of n pairs, pts, that it contains.
  68.  
  69. save textpairs;
  70.  
  71. def textpairs (text t) (suffix pairs_, n_) =
  72.  n_ := 0;
  73.  for q=t:
  74.   pairs_[incr n_] := q;
  75.  endfor;
  76. enddef;
  77.  
  78. % gt - Watch out!  Need to ensure that "p_", etc.,
  79. % don't clash with any name in the passed text "t".
  80. % That's a nasty error to trace!
  81. %
  82. % A name conflict between local variables and variables
  83. % in a text parameter is especially likely in low-level
  84. % utility macros, such as minpair, maxpair and corner.
  85. %
  86. % Unfortunately, we can't *ensure* it won't happen.
  87. % So I appended the underscore to reduce the
  88. % probability of that happening.
  89. %
  90. % Evidently that's why Knuth uses "u_" in "max" and
  91. % "min" in "plain.mf".
  92.  
  93. % gt - corner may be used for finding
  94. % a corner of the bounding box of the
  95. % set of points listed in u and t.
  96. % Other uses may be imaginable. (?)
  97.  
  98. save corner;
  99.  
  100. vardef corner (text xop) (text yop)
  101.               (expr u)(text t) =
  102.   save p_;
  103.   pair p_;
  104.   p_ := u;
  105.   for q=t:
  106.     p_ := (xop (xpart p_, xpart q),
  107.        yop (ypart p_, ypart q));
  108.   endfor;
  109.   p_
  110. enddef;
  111.  
  112. % gt - bottom right, bottom right,
  113. % top left, top right corners.
  114.  
  115. save blpair, brpair, tlpair, trpair;
  116.  
  117. def blpair = corner (min) (min) enddef;
  118. def brpair = corner (max) (min) enddef;
  119. def tlpair = corner (min) (max) enddef;
  120. def trpair = corner (max) (max) enddef;
  121.  
  122. def minpair = blpair enddef;
  123. def maxpair = trpair enddef;
  124.  
  125. % setup
  126. % gt - sets the graphics coordinates.
  127.  
  128. save bounds,
  129.   xneg,xpos,yneg,ypos;
  130.  
  131. def bounds(expr a,b,c,d) = 
  132.  xneg:=a; 
  133.  xpos:=b; 
  134.  yneg:=c; 
  135.  ypos:=d; 
  136. enddef;
  137.  
  138. % conversion
  139.  
  140. save xconv;
  141.  
  142. def xconv(expr xvalue) = 
  143.  ((xvalue-xneg)/(xpos-xneg))*w 
  144. enddef;
  145.  
  146. save unxconv;
  147.  
  148. def unxconv(expr pvalue) = 
  149.  ((pvalue/w)*(xpos-xneg)+xneg) 
  150. enddef;
  151.  
  152. save yconv;
  153.  
  154. def yconv(expr yvalue) = 
  155.  ((yvalue-yneg)/(ypos-yneg))*h 
  156. enddef;
  157.  
  158. save ztr;
  159.  
  160. transform ztr;
  161.  
  162. save setztr;
  163.  
  164. def setztr =
  165.  ztr:=identity
  166.  shifted -(xneg,yneg) 
  167.  xscaled (w/(xpos-xneg))
  168.  yscaled (h/(ypos-yneg));
  169. enddef;
  170.  
  171. % pen width
  172. % in pixel coordinates
  173.  
  174. save penwd;
  175.  
  176. newinternal penwd;
  177.  
  178. % gt - initial value of penwd.
  179.  
  180. interim penwd := 0.5pt;
  181.  
  182. % arrowheads
  183. % in pixel coordinates
  184.  
  185. % hdwdr = arrowhead's ratio of width to length,
  186. % hdten = tension used in drawing its barbs.
  187.  
  188. save hdwdr, hdten;
  189.  
  190. newinternal hdwdr, hdten;
  191.  
  192. % gt - initial values of hdwdr, hdten.
  193.  
  194. interim hdwdr := 1;
  195. interim hdten := 1;
  196.  
  197. % draw an arrowhead.
  198.  
  199. save head, p,side;
  200.  
  201. def head(expr front, back, width, t) =
  202.  pair p[], side;
  203.  side := (width/2) * 
  204.    ((front-back) rotated 90);
  205.  p1 := back + side;
  206.  p2 := back - side;
  207.  draw front{back-front}..tension t..p1;
  208.  draw front{back-front}..tension t..p2;
  209. enddef;
  210.  
  211. % draw an arrowhead of length hlen
  212. % for a path f.
  213.  
  214. save headpath, p;
  215.  
  216. def headpath(expr f,hlen) =
  217.  pair p[];
  218.  p2:=point infinity of f; 
  219.  p1:=direction infinity of f;
  220.  if p1<>(0,0):
  221.   head(p2,p2-(hlen*unitvector(p1)),
  222.     hdwdr,hdten);
  223.  fi;
  224. enddef;
  225.  
  226. % shading and hatching routines
  227. % in pixel coordinates
  228.  
  229. % gt - modified onedot based on
  230. % plain metafont's "drawdot".
  231. % Used in stipple shading.
  232. %
  233. % Note:
  234. % currentpen_path, def_pen_path_,
  235. % t_, and penspeck are defined
  236. % in plain metafont ("plain.mf"
  237. % or "plain.base").
  238.  
  239. save onedot;
  240.  
  241. def onedot(expr p)(suffix v) =
  242.   if unknown currentpen_path:
  243.     def_pen_path_
  244.   fi;
  245.   addto v
  246.     contour currentpen_path
  247.     shifted p.t_
  248.     withpen penspeck
  249. enddef;
  250.  
  251. % gt - draw path f in picture v.
  252. % ("onepath" is the old "onedot",
  253. % but f is intended to be a general path.)
  254. % Used, eg, in hatching and in drawpaths.
  255.  
  256. save onepath;
  257.  
  258. def onepath (expr f) (suffix v) =
  259.   addto v doublepath f
  260.     withpen currentpen; 
  261. enddef;
  262.  
  263. % gt - Paths must be continuous - I think
  264. % - but using suffix, we can pass arrays
  265. % of paths.
  266. %
  267. % My eventual goal is to do as much as
  268. % feasible, and memory-affordable, in
  269. % graphics coordinates, so we can rotate
  270. % and otherwise transform sets of paths
  271. % before drawing.
  272.  
  273. % gt - draw the n paths f[] in picture v.
  274.  
  275. save drawpaths;
  276.  
  277. def drawpaths (expr n) (suffix f, v) =
  278.   for i=1 upto n:
  279.     onepath (f[i], v);
  280.   endfor;
  281. enddef;
  282.  
  283. % clip picture v to interior of path f.
  284.  
  285. save clip;
  286.  
  287. vardef clip(expr f)(suffix v) =
  288.  save vt;
  289.  picture vt;
  290.  vt:=v;
  291.  cull vt keeping (1,infinity);
  292.  addto vt contour f;
  293.  cull vt keeping (2,infinity);
  294.  vt
  295. enddef;
  296.  
  297. % gt - find bounding box of path f.
  298.  
  299. save boundingbox, p;
  300.  
  301. def boundingbox (expr f) (suffix ll, ur) =
  302.   ur := ll := point 0 of f; 
  303.   pair p[];
  304.   for i=0 upto length f:
  305.     p0 := point i of f; 
  306.     p1 := precontrol i of f; 
  307.     p2 := postcontrol i of f;
  308.     ll := minpair (ll, p0, p1, p2);
  309.     ur := maxpair (ur, p0, p1, p2);
  310.   endfor; 
  311. enddef;
  312.  
  313. % gt - shading.
  314.  
  315. % gt - I'm not so happy with dot densities
  316. % over a uniform range.
  317. % Here's code to approximate what may
  318. % be the human eye's light sensitivity.
  319. %
  320. % Mind you, this sort of stuff is done much
  321. % faster in C.
  322.  
  323. save exp;
  324.  
  325. vardef exp (expr x) =
  326.   mexp (256 * x)
  327. enddef;
  328.  
  329. % graya scales the spacing sp;
  330. % grayb scales the graylevel g.
  331.  
  332. save graya, grayb;
  333.  
  334. newinternal graya, grayb;
  335.  
  336. % initial values of gray parameters.
  337.  
  338. interim graya := 0.5 pt;
  339. interim grayb := 3/20;
  340.  
  341. % setgraypars sets gray parameters.
  342. % experimentation is recommended.
  343.  
  344. save setgraypars;
  345.  
  346. def setgraypars (expr a, b) =
  347.   graya := a;
  348.   grayb := b;
  349. enddef;
  350.  
  351. % gt - grayspace gives the dot spacing
  352. % for graylevel g.
  353. %
  354. % Not sure how this model performs.
  355.  
  356. save grayspace;
  357.  
  358. vardef grayspace (expr g) =
  359.   if g <= 1:  % white
  360.     infinity
  361.   elseif g >= 21:  % black
  362.     0
  363.   else:  % gray
  364.     graya / (1 - exp (-g * grayb))
  365.   fi
  366. enddef;
  367.  
  368. % gt - stipple upright box with lower left
  369. % at ll, upper right at ur, in picture v;
  370. % 2sp is dot spacing (rows offset by sp).
  371. %
  372. % NB: "stipple" means "shade with dots",
  373. % if I understand my English dictionary.
  374. %
  375. % Thomas Leathrum devised the trick whereby
  376. % the dots are arranged on a regular grid
  377. % of mesh size sp by sp with the pixel
  378. % origin as one crosspoint.  This ensures
  379. % that objects shaded with the same stipple
  380. % density may be cleanly overlaid.
  381.  
  382. save shadebox, sll, mn, m, n, twosp, p;
  383.  
  384. def shadebox (expr sp, ll, ur) (suffix v) =
  385.   pair sll;
  386.   sll:=sp*(ceilingpair(ll/sp));
  387.   pair mn;
  388.   mn:=floorpair((ur-sll)/sp);
  389.   m:=xpart mn;
  390.   n:=ypart mn;
  391.   twosp:=2sp;
  392.   v:=nullpicture;
  393.   pair p[];
  394.   p2:=sll;
  395.   for i=0 upto m: 
  396.     p3:=p2 if odd i: +(0,sp) fi;
  397.     for j=0 upto n:
  398.       if (not odd (i+j)):
  399.         onedot (p3, v);
  400.         p3:=p3+(0,twosp);
  401.       fi;
  402.     endfor;
  403.     p2:=p2+(sp,0);
  404.   endfor; 
  405. enddef;
  406.  
  407. % stipple interior of closed path f;
  408. % if spacing not positive, fill.
  409.  
  410. save shadepath, ll, ur, v;
  411.  
  412. def shadepath (expr sp, f) =
  413.  if not cycle f: ;
  414.  elseif sp<=0:
  415.    fill f; 
  416.  elseif sp < infinity: 
  417.    pair ll, ur;
  418.    boundingbox (f, ll, ur);
  419.    picture v;
  420.    shadebox (sp, ll, ur, v);
  421.    addto currentpicture 
  422.      also clip(f,v);
  423.  fi;
  424. enddef;
  425.  
  426. % gt - hatch an upright box in picture v,
  427. % with line separation sep x sep.
  428. %
  429. % Notice the similarity to shadebox.
  430.  
  431. save hatchbox, llx, lly, urx, ury, sll,
  432.      mn, m, n, f;
  433.  
  434. def hatchbox (expr sep, ll, ur) (suffix v) =
  435.   llx := xpart ll;
  436.   lly := ypart ll;
  437.   urx := xpart ur;
  438.   ury := ypart ur;
  439.   pair sll;
  440.   sll := sep * ceilingpair (ll/sep);
  441.   pair mn;
  442.   mn := floorpair ((ur-sll)/sep);
  443.   m := xpart mn;
  444.   n := ypart mn;
  445.   v := nullpicture;
  446.   path f;
  447.   f := (xpart sll, lly)--(xpart sll, ury);
  448.   for i=0 upto m:
  449.     onepath (f, v);
  450.     f := f translated (sep, 0);
  451.   endfor;
  452.   f := (llx, ypart sll)--(urx, ypart sll);
  453.   for j=0 upto n:
  454.     onepath (f, v);
  455.     f := f translated (0, sep);
  456.   endfor;
  457. enddef;
  458.  
  459. save hatchpath, ll, ur, v;
  460.  
  461. def hatchpath (expr sep, f) =
  462.  if not cycle f: ;
  463.  elseif sep<=0:
  464.    fill f; 
  465.  elseif sep < infinity: 
  466.    pair ll, ur;
  467.    boundingbox (f, ll, ur);
  468.    picture v;
  469.    hatchbox (sep, ll, ur, v);
  470.    addto currentpicture 
  471.      also clip (f, v);
  472.  fi;
  473. enddef;
  474.  
  475. % gt - shading & hatching macros
  476. % with a syntax like draw, fill,
  477. % unfill and erase.
  478. % sp, sep are in pixel coords,
  479. % f in graphics coordinates;
  480. % f is transformed transparently.
  481.  
  482. save shade;
  483.  
  484. def shade (expr sp) expr f =
  485.   shadepath (sp, f transformed ztr);
  486. enddef;
  487.  
  488. save hatch;
  489.  
  490. def hatch (expr sep) expr f =
  491.   hatchpath (sep, f transformed ztr);
  492. enddef;
  493.  
  494. % gt - common combinations.
  495.  
  496. save drawshade;
  497.  
  498. def drawshade (expr sp) expr f =
  499.   draw f transformed ztr;
  500.   shade (sp) f;
  501. enddef;
  502.  
  503. save drawhatch;
  504.  
  505. def drawhatch (expr sep) expr f =
  506.   draw f transformed ztr;
  507.   hatch (sep) f;
  508. enddef;
  509.  
  510. % * rest of macros start in graphing 
  511. % coordinates but convert to pixel 
  512. % to draw
  513. % * variables ending in "_px" 
  514. % converted to pixel
  515. % * exceptions are the TeX dimensions
  516. % here called:
  517. % ptwd, hlen, dlen, slen, len, sp, sep
  518. % all of which are in pixel coordinates
  519. % * macros beginning with "mk" operate
  520. % entirely in graphing coordinates
  521.  
  522. % general path construction
  523.  
  524. save mkpath;
  525.  
  526. vardef mkpath(expr smooth, cyclic, n)
  527.   (suffix pts) =
  528.  if smooth:
  529.   if cyclic:
  530.    pts[1]{pts[2]-pts[n]}
  531.   else:
  532.    pts[1]
  533.   fi
  534.   for i=2 upto n-1:
  535.    ..pts[i]{pts[i+1]-pts[i-1]}
  536.   endfor
  537.   if cyclic:
  538.    ..pts[n]{pts[1]-pts[n-1]}..cycle
  539.   else:
  540.    ..pts[n]
  541.   fi
  542.  else:
  543.   for i=1 upto n-1:
  544.     pts[i] --
  545.   endfor
  546.   pts[n]
  547.   if cyclic:
  548.    -- cycle
  549.   fi
  550.  fi
  551. enddef;
  552.  
  553. % points, lines, and arrows
  554.  
  555. save pointd, p;
  556.  
  557. def pointd(expr a,ptwd) = 
  558.  pair p_px;
  559.  p_px:=a transformed ztr;
  560.  fill fullcircle scaled ptwd shifted p_px; 
  561. enddef;
  562.  
  563. save line;
  564.  
  565. def line(expr a,b) = 
  566.  draw (a..b) transformed ztr; 
  567. enddef;
  568.  
  569. % gt - arrowpath draws path f
  570. % with an arrowhead;
  571. % hlen is in pixel coordinates;
  572. % f is in graphics coords;
  573. % f is transformed transparently.
  574. % Compare shade, hatch, etc.,
  575. % and contrast shadepath.
  576.  
  577. save arrowpath, f_px;
  578.  
  579. def arrowpath (expr hlen) expr f =
  580.   path f_px;
  581.   f_px := f transformed ztr;
  582.   draw f_px;
  583.   headpath (f_px, hlen);
  584. enddef;
  585.  
  586. % gt - arrow now uses arrowpath.
  587.  
  588. save arrow;
  589.  
  590. def arrow(expr tl,hd,hlen) =
  591.  arrowpath (hlen) tl..hd ;
  592. enddef;
  593.  
  594. % gt - "px" was too frequent
  595. % in dottedline, and made the code
  596. % hard to read, so I've deleted it.
  597. % Only a and b are in graphics coords.
  598.  
  599. save dottedline,
  600.   p, v, l, delta, n;
  601.  
  602. def dottedline (expr a, b, dlen, slen) =
  603.   pair p[];
  604.   p1 := a transformed ztr;
  605.   p3 := b transformed ztr;
  606.   l := length (p3-p1); 
  607.   if (l > 2dlen) and 
  608.     (dlen >= 0) and (slen >= 0): 
  609.   else: 
  610.     pair v;
  611.     v := unitvector (p3-p1);
  612.     n := floor ((l+slen-dlen) / (dlen+slen));
  613.     delta := (l-dlen) / n - (dlen+slen);
  614.     for i=1 upto n:
  615.       p2 := p1 + dlen * v; 
  616.       draw p1..p2; 
  617.       p1 := p2 + (slen+delta) * v;
  618.     endfor; 
  619.   fi;
  620.   draw p1..p3;
  621. enddef;
  622.  
  623. save dottedarrow;
  624.  
  625. def dottedarrow(expr tl,hd,dlen,
  626.   slen,hlen) =
  627.  dottedline(tl,hd,dlen,slen); 
  628.  headpath((tl..hd) transformed ztr,hlen);
  629. enddef;
  630.  
  631. % axes and axis marks
  632.  
  633. save axes;
  634.  
  635. def axes(expr hlen) =
  636.  arrow((0,yneg),(0,ypos),hlen); 
  637.  arrow((xneg,0),(xpos,0),hlen);
  638. enddef;
  639.  
  640. save xmarks;
  641.  
  642. def xmarks(expr len)(text t) =
  643.  for a=t: 
  644.   draw (xconv(a),yconv(0)-(len/2))..
  645.     (xconv(a),yconv(0)+(len/2)); 
  646.  endfor; 
  647. enddef;
  648.  
  649. save ymarks;
  650.  
  651. def ymarks(expr len)(text t) =
  652.  for a=t: 
  653.   draw (xconv(0)-(len/2),yconv(a))..
  654.     (xconv(0)+(len/2),yconv(a)); 
  655.  endfor; 
  656. enddef;
  657.  
  658. % upright rectangles
  659.  
  660. save mkrect;
  661.  
  662. vardef mkrect(expr ll,ur) =
  663.  ll--(xpart ll,ypart ur)--
  664.    ur--(xpart ur,ypart ll)--cycle
  665. enddef;
  666.  
  667. save rect;
  668.  
  669. def rect(expr ll,ur) =
  670.  draw mkrect(ll,ur) transformed ztr;
  671. enddef;
  672.  
  673. save dottedrect;
  674.  
  675. def dottedrect(expr ll,ur,dlen,slen) =
  676.  dottedline(ll,(xpart ll,ypart ur),
  677.    dlen,slen);
  678.  dottedline((xpart ll,ypart ur),ur,
  679.    dlen,slen);
  680.  dottedline(ur,(xpart ur,ypart ll),
  681.    dlen,slen);
  682.  dottedline((xpart ur,ypart ll),ll,
  683.    dlen,slen);
  684. enddef;
  685.  
  686. save block;
  687.  
  688. def block(expr ll,ur) =
  689.  fill mkrect(ll,ur) transformed ztr;
  690. enddef;
  691.  
  692. % gt - rectshade now uses shade.
  693.  
  694. save rectshade;
  695.  
  696. def rectshade(expr sp,ll,ur) =
  697.   shade (sp) mkrect (ll, ur);
  698. enddef;
  699.  
  700. % circles and ellipses
  701.  
  702. save mkellipse;
  703.  
  704. vardef mkellipse(expr center,radx,rady,
  705.   angle) =
  706.  save t;
  707.  transform t; 
  708.  t := identity
  709.    xscaled (2 * radx)
  710.    yscaled (2 * rady)
  711.    rotated angle 
  712.    shifted center;
  713.  fullcircle transformed t
  714. enddef;
  715.  
  716. save ellipse;
  717.  
  718. def ellipse(expr center,radx,rady,
  719.   angle) =
  720.  draw 
  721.    mkellipse(center,radx,rady,angle)
  722.    transformed ztr;
  723. enddef;
  724.  
  725. save circle;
  726.  
  727. def circle(expr center,rad) =
  728.  ellipse(center,rad,rad,0);
  729. enddef;
  730.  
  731. % gt - ellshade now uses shade.
  732.  
  733. save ellshade;
  734.  
  735. def ellshade (expr sp, center, 
  736.   radx, rady, angle) =
  737.  shade (sp)
  738.    mkellipse (center, radx, rady, angle);
  739. enddef;
  740.  
  741. save circshade;
  742.  
  743. def circshade(expr sp, center,rad) =
  744.  ellshade(sp,center,rad,rad,0);
  745. enddef;
  746.  
  747. % circular arcs
  748.  
  749. % gt - mkarc now calculates
  750. % n using ceiling, not floor;
  751. % and saves theta, not i.
  752.  
  753. save mkarc;
  754.  
  755. vardef mkarc(expr center,from,sweep) =
  756.   if sweep=0:
  757.     from
  758.   else:
  759.    begingroup
  760.     save n, theta, p;
  761.     n := 1 + ceiling (abs (sweep) / 45);
  762.     if n<3: n:=3; fi;
  763.     theta:=sweep/(n-1);
  764.     pair p[];
  765.     p1:=from; 
  766.     for i=2 upto n:
  767.      p[i]:=p[i-1] 
  768.        rotatedabout (center,theta);
  769.     endfor;
  770.     mkpath(true,false,n,p)
  771.    endgroup
  772.   fi
  773. enddef;
  774.  
  775. % gt - note that when sweep is a multiple
  776. % of 360 degrees, disp is logically
  777. % infinite, not zero; then the center is
  778. % at infinity.  In practice, arccenter
  779. % ought not to be called in that case.
  780.  
  781. save arccenter;
  782.  
  783. vardef arccenter(expr from,to,sweep) =
  784.  save midpt, disp;
  785.  pair midpt;
  786.  midpt:=(0.5)[from,to];
  787.  disp:=
  788.    if ((sweep mod 360)=0):
  789.     0
  790.    else:
  791.     cosd(sweep/2)/sind(sweep/2)
  792.    fi;
  793.  midpt+(disp*((to-from) rotated 90)/2)
  794. enddef;
  795.  
  796. % gt - mkarcto makes an arc given two points
  797. % on the arc and the sweep angle.
  798. % If sweep is a multiple of 360 degrees,
  799. % then the arc is a straight line;
  800. % if sweep is also nonzero, then that
  801. % line should be infinite, but I use
  802. % from--to instead.
  803.  
  804. save mkarcto;
  805.  
  806. vardef mkarcto (expr from, to, sweep) =
  807.   if from = to:
  808.     from
  809.   elseif (sweep mod 360) = 0:
  810.     from--to
  811.   else
  812.    begingroup
  813.     save center;
  814.     pair center;
  815.     center:=arccenter (from, to, sweep);
  816.     mkarc (center, from, sweep)
  817.    endgroup
  818.   fi
  819. enddef;
  820.  
  821. % gt - arc now uses mkarcto.
  822.  
  823. save arc;
  824.  
  825. def arc(expr from,to,sweep) =
  826.  draw mkarcto (from, to, sweep)
  827.     transformed ztr;
  828. enddef;
  829.  
  830. % gt - arcarrow now uses mkarcto
  831. %      and arrowpath.
  832.  
  833. save arcarrow;
  834.  
  835. def arcarrow(expr hlen,from,to,sweep) =
  836.   arrowpath (hlen) mkarcto (from, to, sweep);
  837. enddef;
  838.  
  839. % gt - mkchordto makes a cyclic path from
  840. % the arc from "from" to "to" with a sweep
  841. % angle of "sweep", and its chord from
  842. % "to" to "from".
  843.  
  844. save mkchordto;
  845.  
  846. vardef mkchordto (expr from, to, sweep) =
  847.   mkarcto (from, to, sweep) -- cycle
  848. enddef;
  849.  
  850. % gt - arcshade now uses mkchordto
  851. %      and shade.
  852.  
  853. save arcshade;
  854.  
  855. def arcshade(expr sp,from,to,sweep) =
  856.   shade (sp) mkchordto (from, to, sweep);
  857. enddef;
  858.  
  859. % gt - three-point arcs.
  860.  
  861. save mkarcthree;
  862.  
  863. vardef mkarcthree (expr first, mid, last) =
  864.   save p, sweep, n, theta, center;
  865.   pair p[];
  866.   p1 := first;
  867.   sweep := 2 (angle (last-mid) - angle (mid-first));
  868.   if abs (sweep) <= 90:
  869.     n := 3;
  870.     p2 := mid;
  871.     p3 := last;
  872.   else:
  873.     n := 1 + ceiling (abs (sweep) / 45);
  874.     theta := sweep / (n-1);
  875.     pair center;
  876.     center := arcthreecenter (first, mid, last);
  877.     for i=2 upto n:
  878.       p[i] := p[i-1] rotatedabout (center, theta);
  879.     endfor;
  880.   fi;
  881.   mkpath (true, false, n, p)
  882. enddef;
  883.  
  884. save arcthreecenter;
  885.  
  886. vardef arcthreecenter (expr first, mid, last) =
  887.   save c, m, d;
  888.   pair c, m[], d[];
  889.   d1 := (mid - first) rotated 90;
  890.   d2 := (last - mid) rotated 90;
  891.   m1 := 0.5 [first, mid];
  892.   m2 := 0.5 [mid, last];
  893.   c = m1 + whatever * d1 = m2 + whatever * d2;
  894.   c
  895. enddef;
  896.  
  897. save arcthree;
  898.  
  899. def arcthree (expr first, mid, last) =
  900.   draw mkarcthree (first, mid, last) transformed ztr;
  901. enddef;
  902.  
  903. save arcthreearrow;
  904.  
  905. def arcthreearrow (expr hlen, first, mid, last) =
  906.   arrowpath (hlen) mkarcthree (first, mid, last);
  907. enddef;
  908.  
  909. % modified polar coordinates
  910.  
  911. % gt - mklinedir makes a path from point "a"
  912. % to a point displaced "len" in direction "theta"
  913. % from "a".
  914.  
  915. save mklinedir;
  916.  
  917. vardef mklinedir (expr a, theta, len) =
  918.   a -- (a + len * (dir theta))
  919. enddef;
  920.  
  921. % gt - linedir now uses mklinedir.
  922.  
  923. save linedir;
  924.  
  925. def linedir(expr a,theta,len) =
  926.   draw mklinedir (a, theta, len)
  927.      transformed ztr;
  928. enddef;
  929.  
  930. % gt - arrowdir now uses mklinedir
  931. %      and arrowpath.
  932.  
  933. save arrowdir;
  934.  
  935. def arrowdir(expr hlen,a,theta,len) =
  936.  arrowpath (hlen)
  937.      mklinedir (a, theta, len);
  938. enddef;
  939.  
  940. % gt - mkarcth makes an arc path with
  941. % given center, radius "rad", initial
  942. % angle "frtheta", and final angle
  943. % "totheta".
  944.  
  945. save mkarcth;
  946.  
  947. vardef mkarcth (expr center,
  948.     frtheta, totheta, rad) =
  949.   save from;
  950.   pair from;
  951.   from := center + rad * (dir frtheta);
  952.   mkarc (center, from, totheta-frtheta)
  953. enddef;
  954.  
  955. % gt - arcth now uses mkarcth.
  956.  
  957. save arcth;
  958.  
  959. def arcth(expr center,
  960.   frtheta,totheta,rad) =
  961.   draw mkarcth (center, frtheta,
  962.         totheta, rad)
  963.     transformed ztr;
  964. enddef;
  965.  
  966. % gt - arcth now uses mkarcth
  967. %      and arrowpath.
  968.  
  969. save arctharrow;
  970.  
  971. def arctharrow(expr hlen,center, 
  972.   frtheta,totheta,rad) =
  973.  arrowpath (hlen)
  974.      mkarcth (center, frtheta,
  975.               totheta, rad);
  976. enddef;
  977.  
  978. % gt - mkwedge makes a wedge-shaped path
  979. % with apex at "center", radius "rad",
  980. % initial angle "frtheta", and final angle
  981. % "totheta".
  982.  
  983. save mkwedge;
  984.  
  985. vardef mkwedge (expr center, frtheta, totheta, rad) =
  986.   center -- mkarcth (from, frtheta, totheta, rad)
  987.          -- cycle
  988. enddef;
  989.  
  990. % gt - wedge draws a sector of a circle.
  991.  
  992. save wedge;
  993.  
  994. def wedge (expr center, frtheta, totheta, rad) =
  995.   draw mkwedge (center, frtheta, totheta, rad)
  996.     transformed ztr;
  997. enddef;
  998.  
  999. % gt - wedgeshade now uses mkwedge and shade.
  1000.  
  1001. save wedgeshade;
  1002.  
  1003. def wedgeshade (expr sp, center, 
  1004.   frtheta, totheta, rad) =
  1005.   shade (sp) mkwedge (center, frtheta, totheta, rad);
  1006. enddef;
  1007.  
  1008. % gt - drawshadewedge draws and shades a wedge.
  1009.  
  1010. save drawshadewedge;
  1011.  
  1012. def drawshadewedge (expr sp, center, 
  1013.   frtheta, totheta, rad) =
  1014.  draw mkwedge (center, frtheta, totheta, rad)
  1015.    transformed ztr;
  1016.  shade (sp) mkwedge (center, frtheta, totheta, rad);
  1017. enddef;
  1018.  
  1019. % curves
  1020.  
  1021. % gt - watch out for that "text containing a local
  1022. % variable's name" conflict!  I dearly wish that
  1023. % weren't a danger.
  1024. % Perhaps it's not so likely at the level of "mkcurve",
  1025. % as the "mk" macros are often fed numeric constants.
  1026.  
  1027. save mkcurve;
  1028.  
  1029. vardef mkcurve(expr smooth,cyclic)
  1030.   (text t) =
  1031.  save n_, p_;
  1032.  pair p_[];
  1033.  textpairs (t) (p_, n_);
  1034.  mkpath(smooth,cyclic,n_,p_)
  1035. enddef;
  1036.  
  1037. save curve;
  1038.  
  1039. def curve(expr smooth,cyclic)
  1040.   (text t) =
  1041.  draw mkcurve(smooth,cyclic,t)
  1042.    transformed ztr; 
  1043. enddef;
  1044.  
  1045. % gt - curvedarrow now uses arrowpath.
  1046.  
  1047. save curvedarrow;
  1048.  
  1049. def curvedarrow(expr smooth,hlen)
  1050.   (text t) =
  1051.   arrowpath (hlen)
  1052.       mkcurve (smooth, false, t);
  1053. enddef;
  1054.  
  1055. % shading of cyclic curves
  1056.  
  1057. % gt - cycleshade now uses shade.
  1058.  
  1059. save cycleshade;
  1060.  
  1061. def cycleshade(expr sp,smooth)(text t) =
  1062.   shade (sp) mkcurve (smooth,true,t);
  1063. enddef;
  1064.  
  1065. % gt - interpolated splines with controls.
  1066.  
  1067. % gt - mkipath uses the interpolation points,
  1068. % p[], and the left and right control points,
  1069. % l[] and r[].
  1070. % Observe that for cyclic I-splines, l[n] is
  1071. % used, not l1, though they are equal; this
  1072. % simplifies the algorithm.
  1073.  
  1074. save mkipath;
  1075.  
  1076. vardef mkipath (expr closed, n)
  1077.   (suffix p, l, r) =
  1078.   for i=1 upto n-1:
  1079.     p[i]..controls r[i] and l[i+1]..
  1080.   endfor
  1081.   if closed:
  1082.     cycle
  1083.   else:
  1084.     p[n]
  1085.   fi
  1086. enddef;
  1087.  
  1088. % gt - mkisplineA uses the I-spline data,
  1089. % in the order that Fig 2.1 gives them,
  1090. % points line pl and control line cl,
  1091. % stores them in p[], l[] and r[],
  1092. % then calls mkipath.
  1093. %
  1094. % pl should have the form:
  1095. %   (x1,y1) ... (xn,yn)
  1096. % and cl the form:
  1097. %   (lx1,ly1) (rx1,ry1) ... (lxn,lyn) (rxn,ryn)
  1098. % which reflect how Fig outputs its data.
  1099. %
  1100. % Don't feed it the "9999 9999", please!
  1101. %
  1102. % Perhaps the input should be massaged by a
  1103. % preprocessor program (e.g. in C), to separate
  1104. % the initially interleaved left and right control
  1105. % points, before being given to graphbase?
  1106. % That would simplify mkisplineA, and run faster.
  1107.  
  1108. save mkisplineA;
  1109.  
  1110. vardef mkisplineA (expr closed)
  1111.   (text pl) (text cl) =
  1112.   save p, l, r, n, i, isleft;
  1113.   pair p[], l[], r[];
  1114.   boolean isleft;
  1115.   textpairs (pl) (p, n);
  1116.   i := 1;
  1117.   isleft := true;
  1118.   for b=cl:
  1119.     if isleft:
  1120.       l[i] := b;
  1121.       isleft := false;
  1122.     else:
  1123.       r[i] := b;
  1124.       i := i+1;
  1125.       isleft := true;
  1126.     fi;
  1127.   endfor;
  1128.   mkipath (closed, n, p, l, r)
  1129. enddef;
  1130.  
  1131. % gt - mkisplineB uses the points line,
  1132. % and the separated left and right controls.
  1133. %
  1134. % See how much simpler this is than
  1135. % mkisplineA.
  1136.  
  1137. save mkisplineB;
  1138.  
  1139. vardef mkisplineB (expr closed)
  1140.   (text pl) (text lc) (text rc) =
  1141.   save p, l, r, n, i;
  1142.   pair p[], l[], r[];
  1143.   textpairs (pl) (p, n);
  1144.   textpairs (lc) (l, i);
  1145.   textpairs (rc) (r, i);
  1146.   mkipath (closed, n, p, l, r)
  1147. enddef;
  1148.  
  1149. % gt - the usual variations.
  1150. %
  1151. % These use mkisplineA.  I'd prefer
  1152. % mkisplineB.
  1153.  
  1154. % draw an interpolated spline,
  1155. % with points line pl and interleaved
  1156. % control line cl.
  1157.  
  1158. save ispline;
  1159.  
  1160. def ispline (expr closed)
  1161.   (text pl) (text cl) =
  1162.   draw mkisplineA (closed) (pl) (cl)
  1163.     transformed ztr;
  1164. enddef;
  1165.  
  1166. save isplinearrow;
  1167.  
  1168. def isplinearrow (expr hlen, closed)
  1169.   (text pl) (text cl) =
  1170.   arrowpath (hlen)
  1171.       mkisplineA (closed) (pl) (cl);
  1172. enddef;
  1173.  
  1174. % gt - isplineshade assumes that the
  1175. % I-spline is closed.
  1176.  
  1177. save isplineshade;
  1178.  
  1179. def isplineshade (expr sp)
  1180.   (text pl) (text cl) =
  1181.   shade (sp)
  1182.       mkisplineA (true) (pl) (cl);
  1183. enddef;
  1184.  
  1185. % functions
  1186.  
  1187. % gt - better be on the safe side with
  1188. % the function text, so use "_" on local
  1189. % variables in "mkfcn".
  1190.  
  1191. save mkfcn;
  1192.  
  1193. vardef mkfcn(expr smooth,bmin,bmax,bst)
  1194.   (suffix bv)(text fcnpr) =
  1195.  save p_, i_;
  1196.  pair p_[];
  1197.  i_ := 0;
  1198.  for bv=bmin step bst 
  1199.    until bmax+(bst/2):
  1200.   p_[incr i_] := fcnpr; 
  1201.  endfor;
  1202.  mkpath (smooth, false, i_ , p_)
  1203. enddef;
  1204.  
  1205. save function;
  1206.  
  1207. def function(expr smooth,xmin,xmax,st)
  1208.   (text fx) =
  1209.  draw mkfcn (smooth, xmin, xmax, st,
  1210.    x, (x,fx))
  1211.    transformed ztr; 
  1212. enddef;
  1213.  
  1214. save parafcn;
  1215.  
  1216. def parafcn(expr smooth,tmin,tmax,st)
  1217.   (text ft) =
  1218.  draw mkfcn (smooth, tmin, tmax, st,
  1219.    t, ft)
  1220.    transformed ztr; 
  1221. enddef;
  1222.  
  1223. % gt - mksfn constructs a path from
  1224. % two functions and the verticals
  1225. % at either side.
  1226. %
  1227. % mksfn is used by shadefcn.
  1228.  
  1229. save mksfn;
  1230.  
  1231. vardef mksfn (expr smooth, xmin, xmax, st)
  1232.     (text fcni) (text fcnii) =
  1233.   mkfcn(smooth,xmin,xmax,st,x,(x,fcni))
  1234.   --
  1235.   reverse
  1236.   mkfcn(smooth,xmin,xmax,st,x,(x,fcnii))
  1237.   -- cycle
  1238. enddef;
  1239.  
  1240. % gt - description:
  1241. % shadefcn shades between two functions over
  1242. % the range xmin to xmax, stepping by st,
  1243. % with dot spacing sp.
  1244. % it does not draw the functions.
  1245.  
  1246. % gt - shadefcn now uses mksfn.
  1247. % I don't see the connection between the dot
  1248. % spacing sp and the function step size st.
  1249.  
  1250. save shadefcn, st;
  1251.  
  1252. def shadefcn(expr sp, xmin, xmax)
  1253.     (text fcni)(text fcnii) =
  1254.   st := unxconv (sp);
  1255.   shade (sp)
  1256.     mksfn (false, xmin, xmax, st) (fcni) (fcnii);
  1257. enddef;
  1258.  
  1259. % gt - drawshadefcn draws both functions fcni
  1260. % and fcnii, and shades between them.
  1261.  
  1262. save drawshadefcn;
  1263.  
  1264. def drawshadefcn (expr sp, smooth, xmin, xmax, st)
  1265.     (text fcni) (text fcnii) =
  1266.   function (smooth, xmin, xmax, st) (fcni);
  1267.   function (smooth, xmin, xmax, st) (fcnii);
  1268.   shadefcn (sp, xmin, xmax) (fcni) (fcnii);
  1269. enddef;
  1270.  
  1271. enddef;  % mfpicenv
  1272.  
  1273. def endmfpicenv =
  1274.  endgroup;
  1275. enddef;
  1276.  
  1277.  
  1278.